﻿using System;
using System.Collections.Generic;
using Lt.Engine.Sampling;
using Lt.Engine.Types;
using Lt.Managers;
using RayDen.Library.Components;
using RayDen.Library.Components.Color;
using RayDen.Library.Components.Surface;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Scene;

namespace Lt.Engine.Data
{

 
    public class SolidMaterial : IBsdf
    {
        private List<BaseBsdf> bxdfs;
        protected internal LtScene scene;

        private MaterialInfo MaterialData;

        public string Name;
        public int ID;


        public ITexture Diffuse, Specular, Bump, Normal, Alpha, Rough;

        public SolidMaterial(MaterialInfo mi, string name = null, int id = 0)
        {
            MaterialData = mi;
            ID = id;
            Name = name;
            bxdfs = new List<BaseBsdf>();
        }

        public void Add(BaseBsdf bsdf)
        {
            this.bxdfs.Add(bsdf);
        }

        public void Setup(RayIntersection isect)
        {
            for (int index = 0; index < bxdfs.Count; index++)
            {
                bxdfs[index].Setup(isect);
            }
        }

        public IRadianceEvaluation Sample(ref Vector wo, ref IRadianceEvaluation radiance, float u0, float u1, float u2, out BsdfSample result, BrdfType flags = BrdfType.All)
        {
            float selPdf;
            BaseBsdf bxdf = GetBsdf(out selPdf, flags);
            
            if (bxdf == null)
            {
                result = new BsdfSample();
                return ColorManager.Instance.ZeroRadiance();
            }

            Vector wo1 = bxdf.WorldToLocal(ref wo);

            var r = bxdf.Sample(ref wo1, ref radiance, u0, u1, u2, out result, flags);
            result.DirectPdf *= selPdf;
            result.OutgoingDirection = bxdf.LocalToWorld(ref result.OutgoingDirection);
            return r;
            /*Vector wi = new Vector();
            var wiW = Vector.Zero;
            var pdf = 0f;
            bool specularBounce;
            if (pdf > 0f && pdf < MathLab.Epsilon) return ColorManager.Instance.ZeroRadiance();

            if (flags != BrdfType.All ) flags = bxdf.Type;
            wiW = bxdf.LocalToWorld(ref wi);
            if ((!bxdf.Type.Has(BrdfType.Specular)) && matchingComps > 1)
            {
                for (int i = 0; i < bxdfs.Count; ++i)
                {
                    if (bxdfs[i] != bxdf &&bxdfs[i].Type.Has(flags))
                        pdf += bxdfs[i].Pdf(scene, ref wo);
                }
            }
            if (matchingComps > 1) pdf /= matchingComps;
            if (bxdf.Type.Has(BrdfType.Specular))
            {
                var f = ColorManager.Instance.ZeroRadiance();
                if ((Vector.Dot(ref wiW , ref bxdf.Ng)) * Vector.Dot(ref wo1 ,ref  bxdf.Ng) > 0f)
                // ignore BTDFs
                {
                    flags = flags & ~BrdfType.Refractive;
                }
                else
                    // ignore BRDFs
                    flags = flags & ~BrdfType.Reflection;
                float cosp, rwp;
                for (int i = 0; i < bxdfs.Count; ++i)
                    if (bxdfs[i].Type.Has(flags))
                        f.Add(bxdfs[i].Evaluate(scene, ref wo, Vector.Dot(ref wiW, ref bxdf.Ng), out cosp, out rwp));

                f.Div(pdf);
            }
            return f;*/
        }

        public IRadianceEvaluation Evaluate(LtScene aScene, ref Vector aWorldDirGen, float oCosThetaGen, out float oDirectPdfW, out float oReversePdfW)
        {
            throw new NotImplementedException();
        }

        public float Pdf(LtScene aScene, ref Vector aWorldDirGen, bool aEvalRevPdf = false)
        {
            throw new NotImplementedException();
        }


        private BaseBsdf GetBsdf(out float pdf, BrdfType flags = BrdfType.All)
        {
            int matchingComps = 0;
            BaseBsdf b = null;
            foreach (var baseBsdf in bxdfs)
            {
                if (BaseBsdf.Has(baseBsdf.Type, flags))
                {
                    if (b == null)
                        b = baseBsdf;
                    matchingComps++;
                }
            }
            pdf = 1f / matchingComps;
            return b;
        }
    }
}
